package com.bestom.waterquality.logic;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.usb.UsbDevice;
import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.bestom.waterquality.R;
import com.bestom.waterquality.WaterQualityApplication;
import com.bestom.waterquality.adapter.DeviceAdapter;
import com.bestom.waterquality.entity.Constant;
import com.bestom.waterquality.entity.DeviceEntity;
import com.bestom.waterquality.entity.RealBean;
import com.bestom.waterquality.entity.SerialEntity;
import com.bestom.waterquality.entity.WarningBean;
import com.bestom.waterquality.services.MQTTService;
import com.bestom.waterquality.services.MQTTService2;
import com.bestom.waterquality.sql.DbHelper;
import com.bestom.waterquality.sql.DbManage;
import com.bestom.waterquality.util.DataTurn;
import com.bestom.waterquality.util.FormatUtil;
import com.bestom.waterquality.util.SharedPreferencesUtil;
import com.bestom.waterquality.util.ToastUtil;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import cn.wch.ch934xlib.CH934XManager;
import cn.wch.ch934xlib.callback.IUsbStateChange;
import cn.wch.ch934xlib.chip.ChipType;
import cn.wch.ch934xlib.chip.Mode;
import cn.wch.ch934xlib.exception.ChipException;
import cn.wch.ch934xlib.exception.NoPermissionException;
import cn.wch.ch934xlib.exception.UartLibException;
import cn.wch.ch934xlib.utils.LogUtil;
import io.reactivex.Observable;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;

/**
 * 串口设置
 * */
public class SerialPortSet {

    private static final String TAG = "Four85Set";
    private SerialPortSet() { }

    private static SerialPortSet _instance = null;

    public static SerialPortSet getInstance() {
        if (_instance == null)
            _instance = new SerialPortSet();
        return _instance;
    }

    private Context mContext;
    private FragmentActivity mActivity;
    private View mView;

    //保存各个串口的接收计数
    HashMap<String, Integer> readCountMap=new HashMap<>();
    //已打开的设备列表
    final Set<UsbDevice> devices= Collections.synchronizedSet(new HashSet<UsbDevice>());
    Thread readThread;
    boolean flag=false;
    private DataTurn mDataTurn;
    private UsbDevice mUsbDevice;

    public UsbDevice getDevice(){
        return mUsbDevice;
    }
    public void setDataAndListen(FragmentActivity activity, Context context, View view){

        if (mActivity == null){
            mActivity = activity;
            mContext = context;
            mView = view;
            mDataTurn = new DataTurn();

            WaterQualityApplication.getApplication().controlKeyboardLayout(view.findViewById(R.id.sp_root),view.findViewById(R.id.sp_scroll));
            if(!UsbFeatureSupported()){
                showToast("系统不支持USB Host功能");
                System.exit(0);
                return;
            }

            initUI();
        }
        enumDevice();
    }

    /**
     * 系统是否支持USB Host功能
     * @return true:系统支持USB Host false:系统不支持USB Host
     */
    public boolean UsbFeatureSupported() {
        return mContext.getPackageManager().hasSystemFeature("android.hardware.usb.host");
    }
    RecyclerView deviceRecyclerVIew;
    DeviceAdapter deviceAdapter;

    void initUI(){
        mView.findViewById(R.id.add_serial).setOnClickListener(v -> showDeviceEntitys());

        deviceRecyclerVIew=mView.findViewById(R.id.rvDevice);

        //初始化recyclerview
        deviceRecyclerVIew.setNestedScrollingEnabled(false);
        deviceAdapter =new DeviceAdapter(mActivity);

        deviceRecyclerVIew.setLayoutManager(new LinearLayoutManager(mContext,LinearLayoutManager.VERTICAL,false));
        View view = LayoutInflater.from(mContext).inflate(R.layout.empty_view, deviceRecyclerVIew, false);
        view.findViewById(R.id.empty_view).setOnClickListener(v -> showDeviceEntitys());
        deviceAdapter.setEmptyView(view);
        deviceRecyclerVIew.setAdapter(deviceAdapter);
        deviceAdapter.setActionListener(this::removeReadDataDevice);

        //监测USB插拔状态
        monitorUSBState();
        //动态申请权限
        if(ContextCompat.checkSelfPermission(mContext, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(mActivity,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},111);
        }
    }

    /**
     * 枚举当前所有符合要求的设备，显示设备列表
     */
    void enumDevice(){
        try {
            //枚举符合要求的设备
            ArrayList<UsbDevice> usbDeviceArrayList = CH934XManager.getInstance().enumDevice();
            if(usbDeviceArrayList.size()==0){
                showToast("no matched devices");
                return;
            }
            for (UsbDevice mUsbDevice : usbDeviceArrayList ){
                open(mUsbDevice);
            }

        } catch (UartLibException e) {
            LogUtil.d(e.getMessage());
        }
    }

    private final HashMap<String, UsbDevice> mUsbDeviceMap = new HashMap<>();
    void showDeviceEntitys(){
        try {
            //枚举符合要求的设备
            ArrayList<UsbDevice> usbDeviceArrayList = CH934XManager.getInstance().enumDevice();
            if(usbDeviceArrayList.size()==0){
                showToast("no matched devices");
                return;
            }
            ArrayList<String> serialName = new ArrayList<>();
            mUsbDeviceMap.clear();
            for (UsbDevice mUsbDevice : usbDeviceArrayList){

                //根据vid/pid获取芯片类型
                ChipType chipType = CH934XManager.getInstance().getChipType(mUsbDevice);
                //获取芯片串口数目,为负则代表出错
                int serialCount = CH934XManager.getInstance().getSerialCount(mUsbDevice);
                //构建recyclerView所绑定的数据,添加设备

                for (int i = 0; i < serialCount; i++) {
                    serialName.add(chipType.getDescription() + "   串口  " + i);
                }
                mUsbDeviceMap.put(chipType.getDescription(),mUsbDevice);
            }
            showMoreAlertDialog(serialName);
        } catch (UartLibException e) {
            LogUtil.d(e.getMessage());
        }
    }
    private AlertDialog alertDialog;
    private final ArrayList<String> mMultiChoiceItems = new ArrayList<>();
    public void showMoreAlertDialog(ArrayList<String> serialName){
        final String[] items = serialName.toArray(new String[0]);
        ArrayList<SerialEntity> serialEntities=new ArrayList<>();
        mMultiChoiceItems.clear();
        AlertDialog.Builder alertBuilder = new AlertDialog.Builder(mContext);
        alertBuilder.setTitle("选择添加串口");
        /*
         *第一个参数:弹出框的消息集合，一般为字符串集合
         * 第二个参数：默认被选中的，布尔类数组
         * 第三个参数：勾选事件监听
         */
        alertBuilder.setMultiChoiceItems(items, null, (dialogInterface, i, isChecked) -> {
            //构建recyclerView所绑定的数据,添加设备
            if (isChecked){
                mMultiChoiceItems.add(items[i]);
            }else {
                mMultiChoiceItems.remove(items[i]);
            }
        });
        alertBuilder.setPositiveButton("确定", (dialogInterface, i) -> {
            alertDialog.dismiss();
            UsbDevice usbDevice = null;
            String description = null;
            for(String item : mMultiChoiceItems){
                String[] split = item.split("  ");
                description = split[0].trim();
                usbDevice = mUsbDeviceMap.get(description);
                SerialEntity serialEntity = new SerialEntity(usbDevice,Integer.parseInt(split[2].trim()));
                serialEntities.add(serialEntity);
            }

            if (usbDevice == null || description == null) {
                return;
            }
            DeviceEntity deviceEntity = new DeviceEntity(usbDevice, description, serialEntities);
            deviceAdapter.addDevice(deviceEntity);
        });

        alertBuilder.setNegativeButton("取消", (dialogInterface, i) -> alertDialog.dismiss());
        alertDialog = alertBuilder.create();
        alertDialog.show();
    }

    /**
     * 从设备列表中打开某个设备
     */
    void open(@NonNull UsbDevice usbDevice){
        if(CH934XManager.getInstance().isConnected(usbDevice)){
//            showToast("当前设备已经打开");
            return;
        }
        try {
            boolean b = CH934XManager.getInstance().openDevice(usbDevice);
            if(b){
                //打开成功
                readThread=new ReadThread();
                readThread.start();
                //更新显示的ui
                //update(usbDevice);
                //初始化接收计数
                int serialCount = CH934XManager.getInstance().getSerialCount(usbDevice);
                for (int i = 0; i < serialCount; i++) {
                    readCountMap.put(FormatUtil.getSerialKey(usbDevice,i),0);
                }
                //将该设备添加至已打开设备列表,在读线程ReadThread中,将会读取该设备的每个串口数据
                addToReadDeviceSet(usbDevice);
                //用作文件对比测试,在打开每个设备时，对每个串口新建对应的保存数据的文件
               /* for (int i = 0; i < serialCount; i++) {
                    linkSerialToFile(usbDevice,i);
                }*/
                mUsbDevice = usbDevice;
                for (int i = 0; i < 4; i++){
                    getCurrentMode(i, mUsbDevice);
                }

            }else {
                showToast("打开失败");
            }
        } catch (ChipException e) {
            LogUtil.d(e.getMessage());
        } catch (NoPermissionException e) {
            //没有权限打开该设备
            //申请权限
            showToast("没有权限打开该设备");
            requestPermission(usbDevice);
        } catch (UartLibException e) {
            e.printStackTrace();
        }
    }

    /**获取设备模式*/
    private void getCurrentMode(int serialNumber, UsbDevice mUsbDevice){
        try {
            Mode currentMode = CH934XManager.getInstance().getCurrentMode(mUsbDevice, serialNumber);
            if(currentMode!=null){

                if(currentMode==Mode.HARDFLOW){
                    ToastUtil.toast("close flow first!");
                }else if (currentMode==Mode.NORMAL){ // 若为NORMAL，则执行使能 GPIO
                    enableGPIO(mUsbDevice, serialNumber, true);
                }
            }
        } catch (UartLibException e) {
            e.printStackTrace();
        }
    }
    /**使能 GPIO*/
    private void enableGPIO(UsbDevice device, int serialNumber, boolean enabled){
        Observable.create((ObservableOnSubscribe<String>) emitter -> {

            try {
                boolean ret = CH934XManager.getInstance().enableGPIO(device, serialNumber, enabled);
                if(!ret){
                    emitter.onError(new Throwable("operation failed!"));
                }else {
                    emitter.onComplete();
                }
            } catch (UartLibException e) {
                emitter.onError(new Throwable(e.getMessage()));
            }

        })
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<String>() {
                    @Override
                    public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) {
                    }

                    @Override
                    public void onNext(@io.reactivex.annotations.NonNull String s) {

                    }

                    @Override
                    public void onError(@io.reactivex.annotations.NonNull Throwable e) {
                        ToastUtil.toast(e.getMessage());
                    }

                    @Override
                    public void onComplete() {
                    }
                });
    }

    /**
     * 申请读写权限
     */
    private void requestPermission(@NonNull UsbDevice usbDevice){
        try {
            CH934XManager.getInstance().requestPermission(mContext,usbDevice);
        } catch (UartLibException e) {
            LogUtil.d(e.getMessage());
        }
    }

    /**
     * 监测USB的状态
     */
    private void monitorUSBState(){
        CH934XManager.getInstance().setUsbStateListener(new IUsbStateChange() {
            @Override
            public void usbDeviceDetach(UsbDevice device) {
                //设备移除
                mActivity.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        //从界面上移除
                        if(deviceAdapter!=null){
                            deviceAdapter.removeDevice(device);
                        }
                    }
                });
            }

            @Override
            public void usbDeviceAttach(UsbDevice device) {
                //设备插入
                showToast("found new devices:" + device.getDeviceName());
                enumDevice();
            }

            @Override
            public void usbDevicePermission(UsbDevice device, boolean result) {
                //请求打开设备权限结果
                if (result){
                    enumDevice();
                }
            }
        });
    }

    /**
     * 关闭所有设备
     */
    void closeAll(){
        ArrayList<UsbDevice> usbDeviceArrayList = null;
        try {
            usbDeviceArrayList = CH934XManager.getInstance().enumDevice();
            for (UsbDevice usbDevice : usbDeviceArrayList) {
                if(CH934XManager.getInstance().isConnected(usbDevice)){
                    CH934XManager.getInstance().disconnect(usbDevice);
                }
            }
        } catch (UartLibException e) {
            e.printStackTrace();
        }
    }

    private void addToReadDeviceSet(@NonNull UsbDevice usbDevice){
        synchronized (devices){
            devices.add(usbDevice);
        }

    }

    private void removeReadDataDevice(@NonNull UsbDevice usbDevice){
        synchronized (devices){
            devices.remove(usbDevice);
        }
    }
    public class ReadThread extends Thread{

        public ReadThread() {
            flag=true;
            setPriority(Thread.MAX_PRIORITY);
        }

        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public void run() {
            LogUtil.d("---------------开始读取数据");
            while (flag){
                if(devices.isEmpty()){
                    continue;
                }
                //遍历已打开的设备列表中的设备
                synchronized (devices){
                    for (UsbDevice device : devices) {
                        int serialCount = CH934XManager.getInstance().getSerialCount(device);
                        //读取该设备每个串口的数据

                        for (int i = 0; i < serialCount; i++) {
                            try {
                                if (deviceAdapter.getDeviceListCount() == 0) {
                                    try {
                                        Thread.sleep(500);
                                    } catch (InterruptedException ex) {
                                    }
                                    break;
                                }

                                /*
                                 * 逻辑说明：
                                 * 警戒值 = 平均值 + 上下浮动值， 未要求做低于警戒值处理
                                 * 以下分别判断处理三类传感器的返回值（若新加传感器，判断方式存在重复则根据实际修改）
                                 * PH 和 ORP 传感器：若大于警戒值，则抓拍图片并上报数据，并更新预警记录界面；反之仅做数据记录入数据库
                                 * VCP 传感器：若大于警戒值，则抓拍图片并上报数据，并更新预警记录界面；反之做数据记录入数据库，并更新图片查看界面
                                 * */
                                byte[] bytes = CH934XManager.getInstance().readData(device, i);
                                if (bytes != null) {
                                    LogUtil.d("串口" + i + "收到数据:---->length:" + bytes.length + "  " + FormatUtil.bytesToHexString(bytes));
                                    // 获取传感器设置的预警浮动值
                                    float floatValue = Float.parseFloat(String.valueOf(SharedPreferencesUtil.getInstance().getValue(Constant.warningValue, "15.0")));
                                    if (bytes.length == 9) {
                                        byte[] data = new byte[4];
                                        data[0] = bytes[6];
                                        data[1] = bytes[5];
                                        data[2] = bytes[4];
                                        data[3] = bytes[3];
                                        float aFloat = mDataTurn.ByteArrToFloat(data);
                                        String hex = mDataTurn.Byte2Hex(bytes[0]);
                                        String timeStr1 = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                                        long millis = System.currentTimeMillis();
                                        if (hex.equals("02")) {
                                            // 不符合正常值，不作往下处理
                                            if (aFloat > 14 || aFloat < 0) continue;
                                            if (aFloat > WaterQualityApplication.getApplication().getPHAvg() * (1 + floatValue / 100)) {
                                                if (WaterQualityApplication.getApplication().getPHAvg() == 0.0f) {
                                                    // 若是第一天，则不判断，仅记录
                                                    DbManage.getInstance(mContext).addORPOrPHOrVcpData(DbHelper.PH_Record_NAME, timeStr1, String.valueOf(aFloat));
                                                } else {
                                                    String path = "";
                                                    if (RTSPSet.getInstance().SnapPicture(Constant.warningImageDir, millis))
                                                        path = millis + ".jpg";
                                                    DbManage.getInstance(mContext).addWarningRecord(timeStr1, "PH:" + aFloat, path);

                                                    WarningBean bean = new WarningBean(timeStr1, "PH:" + aFloat, path);
                                                    WarningRecord.getInstance().addWarningData(bean);
                                                    if (WaterQualityApplication.mtIsOk) {
                                                        MQTTService.publish(Constant.DataPublishTopic, "02", "PH:" + aFloat, Constant.warningImageDir + millis + ".jpg");
                                                    }
                                                    if (WaterQualityApplication.mt2IsOk) {
                                                        MQTTService2.publish(Constant.DataPublishTopic, "02", "PH:" + aFloat, Constant.warningImageDir + millis + ".jpg");
                                                    }
                                                }
                                            } else {
                                                DbManage.getInstance(mContext).addORPOrPHOrVcpData(DbHelper.PH_Record_NAME, timeStr1, String.valueOf(aFloat));
                                            }
                                        } else if (hex.equals("01")) {
                                            // 不符合正常值，不作往下处理
                                            if (aFloat > 2000 || aFloat < -2000) continue;
                                            if (aFloat > WaterQualityApplication.getApplication().getORPAvg() * (1 + floatValue / 100)) {
                                                if (WaterQualityApplication.getApplication().getORPAvg() == 0.0f) {
                                                    // 若是第一天，则不判断，仅记录
                                                    DbManage.getInstance(mContext).addORPOrPHOrVcpData(DbHelper.ORP_Record_NAME, timeStr1, String.valueOf(aFloat));
                                                } else {
                                                    String path = "";
                                                    if (RTSPSet.getInstance().SnapPicture(Constant.warningImageDir, millis))
                                                        path = millis + ".jpg";
                                                    DbManage.getInstance(mContext).addWarningRecord(timeStr1, "ORP:" + aFloat, path);

                                                    WarningBean bean = new WarningBean(timeStr1, "ORP:" + aFloat, path);
                                                    WarningRecord.getInstance().addWarningData(bean);

                                                    if (WaterQualityApplication.mtIsOk) {
                                                        MQTTService.publish(Constant.DataPublishTopic, "02", "PH:" + aFloat, Constant.warningImageDir + millis + ".jpg");
                                                    }
                                                    if (WaterQualityApplication.mt2IsOk) {
                                                        MQTTService2.publish(Constant.DataPublishTopic, "02", "PH:" + aFloat, Constant.warningImageDir + millis + ".jpg");
                                                    }
                                                }
                                            } else {
                                                DbManage.getInstance(mContext).addORPOrPHOrVcpData(DbHelper.ORP_Record_NAME, timeStr1, String.valueOf(aFloat));
                                            }
                                        }
                                        LogUtil.d("串口" + i + "收到数据:---->length:" + bytes.length + "  " + FormatUtil.bytesToHexString(bytes) + "   aFloat--->" + aFloat);
                                    } else if (bytes.length == 45) {
                                        byte[] vcpByte = new byte[4];
                                        vcpByte[0] = bytes[4];
                                        vcpByte[1] = bytes[3];
                                        vcpByte[2] = bytes[6];
                                        vcpByte[3] = bytes[5];
                                        float vcp = (float)(Math.round(mDataTurn.ByteArrToFloat(vcpByte)*100)/100);
                                        // 不符合正常值，不作往下处理
                                        if (vcp > 2000 || vcp < 0) continue;
                                        byte[] tempByte = new byte[4];
                                        tempByte[0] = bytes[8];
                                        tempByte[1] = bytes[7];
                                        tempByte[2] = bytes[10];
                                        tempByte[3] = bytes[9];
                                        float temp = (float)(Math.round(mDataTurn.ByteArrToFloat(tempByte)*100)/100);
                                        LogUtil.d("收到数据 : vcp--->" + vcp + "   temp--->" + temp);
                                        String timeStr1 = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                                        long millis = System.currentTimeMillis();
                                        if (vcp > WaterQualityApplication.getApplication().getVCPAvg() * (1 + floatValue / 100)) {
                                            if (WaterQualityApplication.getApplication().getVCPAvg() == 0.0f) {
                                                // 若是第一天，则不判断，仅记录

                                                DbManage.getInstance(mContext).addORPOrPHOrVcpData(DbHelper.VCP_Record_NAME, timeStr1, String.valueOf(vcp));

                                                mVcp = String.valueOf(vcp);
                                                mTemp = String.valueOf(temp);

                                                /*String path = "";
                                                if (RTSPSet.getInstance().SnapPicture(Constant.realImageDir, millis))
                                                    path = millis + ".jpg";
                                                DbManage.getInstance(mContext).addRealRecord(timeStr1, String.valueOf(vcp), String.valueOf(temp), path);

                                                RealBean bean = new RealBean(timeStr1, String.valueOf(vcp), String.valueOf(temp), path);
                                                ImageViewer.getInstance().addRealData(bean);*/
                                            } else {
                                                String path = "";
                                                if (RTSPSet.getInstance().SnapPicture(Constant.warningImageDir, millis))
                                                    path = millis + ".jpg";
                                                DbManage.getInstance(mContext).addWarningRecord(timeStr1, "VCP:" + vcp + ", TEMP:" + temp, path);

                                                WarningBean bean = new WarningBean(timeStr1, "VCP:" + vcp + ", TEMP:" + temp, path);
                                                WarningRecord.getInstance().addWarningData(bean);

                                                if (WaterQualityApplication.mtIsOk) {
                                                    MQTTService.publish(Constant.DataPublishTopic, "02", "VCP:" + vcp + ", TEMP:" + temp, Constant.warningImageDir + millis + ".jpg");
                                                }
                                                if (WaterQualityApplication.mt2IsOk) {
                                                    MQTTService2.publish(Constant.DataPublishTopic, "02", "VCP:" + vcp + ", TEMP:" + temp, Constant.warningImageDir + millis + ".jpg");
                                                }
                                            }
                                        } else {

                                            DbManage.getInstance(mContext).addORPOrPHOrVcpData(DbHelper.VCP_Record_NAME, timeStr1, String.valueOf(vcp));

                                            mVcp = String.valueOf(vcp);
                                            mTemp = String.valueOf(temp);

                                            /*String path = "";
                                            if (RTSPSet.getInstance().SnapPicture(Constant.realImageDir, millis))
                                                path = millis + ".jpg";
                                            DbManage.getInstance(mContext).addRealRecord(timeStr1, String.valueOf(vcp), String.valueOf(temp), path);

                                            RealBean bean = new RealBean(timeStr1, String.valueOf(vcp), String.valueOf(temp), path);
                                            ImageViewer.getInstance().addRealData(bean);*/
                                        }
                                    }
                                }
                                try {
                                    Thread.sleep(200);
                                } catch (InterruptedException ex) {
                                }
                            } catch (ChipException e) {
                                //LogUtil.d(e.getMessage());
                                break;
                            }
                        }
                    }
                }
            }
            LogUtil.d("读取数据线程结束");
        }
    }
    private String mVcp = "";
    public String getRealVcp(){
        return mVcp;
    }

    private String mTemp = "";
    public String getRealTemp(){
        return mTemp;
    }

    public void stopReadThread(){
        if(readThread!=null && readThread.isAlive()){
            flag=false;
        }
    }

    private void showToast(String message){
        mActivity.runOnUiThread(() -> ToastUtil.create(mContext,message).show());
    }

}
